#include "Task.hpp"
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <assert.h>
#include <wait.h>

const int processnum = 10;
std::vector<task_t> tasks;

// 先描述
class channel
{
public:
    channel(int cmdfd, int slaverid, const std::string &processname)
        : _cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
    {
    }

public:
    int _cmdfd;               // 发送任务的文件描述符
    pid_t _slaverid;          // 子进程的PID
    std::string _processname; // 子进程的名字 -- 方便我们打印日志
};

void slaver()
{
    // read(0)
    while (true)
    {
        int cmdcode = 0;
        // ssize_t read(int fd, void *buf, size_t count);
        int n = read(0, &cmdcode, sizeof(int));
        if (n == sizeof(int))
        {
            // 执行cmdcode对应的任务列表
            std::cout << "slaver say@ get a command: " << getpid() << " : cmdcode: " << cmdcode << std::endl;
            if (cmdcode >= 0 && cmdcode < tasks.size())
                tasks[cmdcode]();
        }
        if (n == 0)
            break; // 读端正常读，写端关闭，读端就会读到0，表明读到了文件（pipe）结尾，不会被阻塞
    }
}
// 输入：const &
// 输出：*
// 输入输出：&
void InitProcessPool(std::vector<channel> *channels)
{
    // version2:确保每一个子进程都只有一个写端
    // 增加部分1
    std::vector<int> oldfds; // 创建一个数组，记录在某一刻创建文件的文件描述符
    for (int i = 0; i < processnum; i++)
    {
        int pipefd[2]; // 临时空间
        int n = pipe(pipefd);
        assert(!n); // ASSERT()在Debug版本中有效，Release版本会被忽略
        (void)n;    // 显式告诉编译器该变量被有意未使用，避免编译警告

        pid_t id = fork();
        if (id == 0)
        {
            // 增加部分2
            // 关闭当前子进程对前面子进程的写端
            std::cout << "child: " << getpid() << " close history fd: ";
            for (auto fd : oldfds)
            {
                std::cout << fd << " ";
                close(fd);
            }
            std::cout << "\n";

            close(pipefd[1]);
            // int dup2(int oldfd, int newfd);
            dup2(pipefd[0], 0); // 重定向读端
            close(pipefd[0]);
            slaver();
            std::cout << "process : " << getpid() << " quit" << std::endl;

            exit(0);
        }

        // father
        close(pipefd[0]);
        // 添加channel字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));

        // 增加部分3
        oldfds.push_back(pipefd[1]); // 记录当前子进程的写端
        sleep(1);
    }
}

void Debug(const std::vector<channel> &channels)
{
    // test
    for (const auto &c : channels)
    {
        std::cout << c._cmdfd << " " << c._slaverid << " " << c._processname << std::endl;
    }
}
void Menu()
{
    std::cout << "################################################" << std::endl;
    std::cout << "# 1. 刷新日志             2. 刷新出来野怪        #" << std::endl;
    std::cout << "# 3. 检测软件是否更新      4. 更新用的血量和蓝量  #" << std::endl;
    std::cout << "#                         0. 退出               #" << std::endl;
    std::cout << "#################################################" << std::endl;
}

void ctrlSlaver(const std::vector<channel> &channels)
{
    // 轮询选择进程
    int which = 0;
    while (true)
    {
        int select = 0;
        Menu();
        std::cout << "Please Enter@";
        std::cin >> select;

        if (select <= 0 || select >= 5)
            break;

        // 1.选择任务
        int cmdcode = select - 1;

        std::cout << "father say: " << " cmdcode: " << cmdcode << " already sendto " << channels[which]._slaverid << " process name: "
                  << channels[which]._processname << std::endl;

        // 2.发送任务
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));

        which++;
        which %= channels.size();

        sleep(1);
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    // 文件所有写端被关闭后，读端读取到0，子进程退出，最后一个创建的进程先退出
    // 后创建的进程先退出
    //  for(const auto &c : channels) close(c._cmdfd);
    //  sleep(5);
    //  for(const auto &c : channels) waitpid(c._slaverid,nullptr,0);
    //  sleep(5);
    for (const auto &c : channels)
    {
        close(c._cmdfd);
        waitpid(c._slaverid, nullptr, 0);
    }
}
int main()
{
    LoadTask(&tasks);

    srand(time(nullptr) ^ getpid() ^ 1023); // 种一个随机数种子

    // 在组织
    std::vector<channel> channels;

    // 1. 初始化，存在bug
    InitProcessPool(&channels);

    // Debug(channels);
    // 2.开始控制子进程
    ctrlSlaver(channels);

    // 3.清理收尾
    QuitProcess(channels);

    return 0;
}
